繼續來看前一天的文件
此功能是擴增 pods status 的欄位,可以自訂欄位名稱,且只要當此欄位尚未 Ready 時,pods 的 Ready 欄位就會是 false
此欄位的判斷方式就是透過 API 調整欄位啟用,預設是關閉的。
也就是說,判斷的邏輯規則是透過外部的 APP 來處理的。
pod readiness 的功能從 k8s 1.14 之後就進入 stable 的狀態,不過筆者是沒特別看過太多使用情境,翻找他人的範例都是透過自己寫程式的方式來讓此 status 改為 true
文件內有特別提到,不能使用kubectl patch
的方式讓 pod readiness 通過,必須要透過 PATCH 的 action 打 apiserver 才行
嘗試看看此功能,在一個 nginx 的 deployment 加上:
...
spec:
template:
spec:
readinessGates:
- conditionType: "www.example.com/gate-1"
...
然後會發現,所有 pods 啟動時都是 1/1 Ready 的狀態,後面則多出了 readiness gate 的欄位
查看 endpoint 沒有轉送流量到 pods
❯ k get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-test-ff55c4d84-547jt 1/1 Running 0 9s 10.0.2.5 k8s-master3 <none> 0/1
nginx-test-ff55c4d84-rrrvj 1/1 Running 0 9s 10.0.0.213 k8s-master1 <none> 0/1
nginx-test-ff55c4d84-sr8x8 1/1 Running 0 9s 10.0.2.83 k8s-master3 <none> 0/1
nginx-test-ff55c4d84-thwlb 1/1 Running 0 9s 10.0.2.48 k8s-master3 <none> 0/1
nginx-test-ff55c4d84-xdbm2 1/1 Running 0 9s 10.0.0.17 k8s-master1 <none> 0/1
❯ k get ep
NAME ENDPOINTS AGE
kubernetes 192.168.75.11:6443,192.168.75.13:6443 3d5h
nginx-test 12m
參考他人文章,使用 kubectl proxy
開啟一個 port,後續指定某 pods name 透過 curl 發送請求修改內容:
❯ kubectl proxy --port 12345 &
❯ curl -k \
-H "Content-Type: application/json-patch+json" \
-X PATCH http://localhost:12345/api/v1/namespaces/default/pods/nginx-test-ff55c4d84-547jt/status \
--data '[{ "op": "replace", "path": "/status/conditions/0",
"value": { "type": "www.example.com/gate-1", "status": "True" }}]'
❯ k get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-test-ff55c4d84-547jt 1/1 Running 0 3m34s 10.0.2.5 k8s-master3 <none> 1/1
nginx-test-ff55c4d84-rrrvj 1/1 Running 0 3m34s 10.0.0.213 k8s-master1 <none> 0/1
nginx-test-ff55c4d84-sr8x8 1/1 Running 0 3m34s 10.0.2.83 k8s-master3 <none> 0/1
nginx-test-ff55c4d84-thwlb 1/1 Running 0 3m34s 10.0.2.48 k8s-master3 <none> 0/1
nginx-test-ff55c4d84-xdbm2 1/1 Running 0 3m34s 10.0.0.17 k8s-master1 <none> 0/1
❯ k get ep
NAME ENDPOINTS AGE
kubernetes 192.168.75.11:6443,192.168.75.13:6443 3d5h
nginx-test 10.0.2.5:80 30m
讓其中一個 pods 的 readiness gate 通過後,就會發現 service 才會相對應轉送流量過去
這個功能應該是要開發 operator 的人員會比較可能用到,透過外部的機制來把控 APP 是否可用。
不過筆者對這個功能真的不熟,只是試用看看,或許有更方便使用的應用場景 ~
在以前的版本中,此狀態又被稱作 PodHasNetwork
主要是描述 PodReadyToStartContainersCondition
的這個狀態是如何判斷的,當 kubelet 正常啟用 pods 後,此欄位即為 True,為 False 時通常是以下情況:
在此狀態設定為 True 之後,kubelet 才會開始去拉 container image (還蠻有趣的先後順序)
當 pods 有設定 initcontainer
時,會先建立 sandbox container,再執行 initcontainer
,當 initcontainer
建立完成後。才會把 Initialized
設為 True
若無 initcontainer,則會在 sandbox container 建立之前,就將 Initialized
設為 True (算是無傷大雅的順序?)
若要讓部署在 k8s 上的 App 有更完善的健康管理機制,可以考慮加入 probe(探針)
檢查的方式有以下幾種:
exec
:指定執行 linux 上的指令,當 exit status code 為 0 時就代表通過。grpc
:發送 grpc health checks 到某個 service,只要回傳 SERVING
就代表通過httpGet
:發送 Http Get 到某個 endpoint 確認是否有通,當回傳的代碼介於 200 ~ 399 之間代表通過tcpSocket
:檢查某個 TCP port 是否有通,只需要 port 有連通就代表通過官方建議在使用 exec probe 時要小心,當 pods 數量很多且
initialDelaySeconds
,periodSeconds
設定較小時,很可能會增加節點的 cpu 使用率。
在這樣的情況下,建議採取其他的偵測機制避免過多的資源浪費。
probes 會有三種的執行結果:
Success
:通過診斷Failure
:未通過診斷Unknown
:診斷失敗,且 kubelet 會持續追查問題 (使用者不需要執行動作)這幾個狀態應該都蠻容易理解的
可以設定三種不同類型的 probe 到 container 上:
livenessProbe
:確保 containers 活著,當 livenessProbe 診斷失敗,kubelet 會刪掉此 container,並以 restartpolicy
決定是否重啟。readinessProbe
: 確保 containers 已經可以接受流量。當 readinessProbe 未通過診斷時,endpoint controller 會在 endpoint 移除該 pods IP 避免 service 轉送流量至未準備好的 container。startupProbe
:當 containers 剛開啟時套用,當 startupProbe
還在診斷階段時,其他的 Probe 都還不會運作。當 startupProbe 診斷失敗,kubelet 會刪掉此 container,並以 restartpolicy
決定是否重啟。至於總共要判斷幾次,多久內會失敗則是透過以下幾個參數:
initialDelaySeconds
:經過多少秒數後,才會開始偵測failureThreshold
:總共失敗幾次才會算失敗 (預設為 3)periodSeconds
:多久偵測一次 (預設為 10s)successThreshold
:僅能設定於 readiness probe,probe 成功幾次才算 ready (liveness 與 startup 必須為 1)timeoutSeconds
:probe 執行超過多久就算是 timeout (預設為 1s)也就是說總共的失敗時間計算方式為:
initialDelaySeconds + failureThreshold × periodSeconds
在 https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ 的文件中有詳細介紹 Probe 的用法,可以看其中以下的一個範例:
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: registry.k8s.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
此範例設定了 readinessProbe
與 livenessProbe
,且都是偵測此 container 的 TCP 8080 port 是否可用,當 8080 port 不可用且持續一段時間後,livenessProbe
就會觸發 kubelet 將 container 移除。
liveness probe 觸發時,kubelet 會嘗試將 container 移除,通常是為了避免 container 有 “活著但無法提供服務” 的現象發生,才會需要配置 liveness probe,否則若是 APP 本身的 error handling 都有正確配置的話,或許可以考慮不需要加入 liveness probe。
liveness probe 觸發時,exit status 會回傳 error,也就是說當 container 設定 restartPolicy
為 OnFailure
時也會重啟。
readiness probe 是用來確保 container 已經準備好接受流量,使用情境大致有:
當 pods 在接收到 delete 的指令時,會預設將內部所有的 container 設為 not ready 的狀態,也就是在關閉期間會停止轉送流量過去。
因此在這樣的情境下就不會是 readiness probe 考量的情境。
當 container 被設計會需要較長的啟動時間時,設定 startup probe 可以確保 container 正常啟動後,才開始後續的動作 (e.g. readiness probe 才開始偵測)
不過在設計 startup probe 時要配置好足夠的時間讓 APP 啟動,否則可能 APP 會一直卡在被 startup probe 持續刪掉的無限循環中。
若要讓 pods 內的 APP 正常退出,就不能僅僅只是送 KILL 訊號導致 process 忽然中斷,這可能會造成資料的損毀或請求傳送不完全等等
常見的 Unix 離開訊號有兩種:
kubelet 預設會嘗試 graceful shutdown,大致流程為:
kubectl delete
指定 pods(預設會帶入 grace period 30s)kubectl describe
去看會發現 pods 會顯示 “Terminating” 的狀態terminationGracePeriodSeconds
不為 0 (預設為 30s),則 kubelet 會在 container 內執行此 hook要強制把 pods 刪除,可以使用:
kubectl delete pods <pod-name> --force --grace-period=0
這樣做會立刻從 apiserver 上面將該 pods 刪除,不會等 kubelet 確認 pods 刪除,因此即使 kubectl get 看不到該 pods,此 pods 還是有可能持續在該 node 上運行
另外要注意強制刪除由 statefulset 有特別的流程要注意 (主要是 statefulset 的 pods 名稱是固定的,因此要特別小心)
https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
因為當 node 進入 “unreachable” 的狀態時,並不會自動關閉 pods,當 user 手動刪除時,pods 的狀態會停在 'Terminating' or 'Unknown’,當 pods 進入這兩種情況時,有三種行為可以讓 pods 刪除:
官方是建議用 1,2 兩種,因為 3 暴力刪除很可能導致 statefulset 異常
但若是 force 刪除之後,還是有異常的話,就必須再加上:
# 原本的指令
# kubectl delete pods <pod> --grace-period=0
# 再加上
kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'
不過這樣刪除似乎 “強烈不建議”,還是建議把節點救回來 or 直接把節點刪除
在 k8s node 失聯後,雖然可以看到 node 的狀態會轉成 NotReady,但是該節點上的 pods 卻還是會保持著 running 狀態,
雖然在 statefulset force delete 的這篇文章有寫到說:當 node unreachable 超過一定時間後,該 node 上的 pods 狀態都會 Ready → Unknown,但筆者目前的測試 cluster 並不會這樣處理,將 node 關機一天後,pods 依然是 Running 狀態,只有當 Node 重新開機後,會發現 pods 的 restart 次數 +1
等到後續看到 controller-manager 的機制再來深入研究研究…
當 pods 有 sidecar containers 存在時,刪除時 sidecar containers 會是最後才接受到 SIGTERM 訊號的 container
不過要是整個 pods 的刪除時間過長,超過了 graceperiod 的話,kubelet 還是會同時對所有的 container 發送 SIGKILL 強制關閉訊號
同理當有設定 preStop hook 且導致時間超過的話,也是會整個 pods 同時刪除
在 control plane 會有一個 Pod garbage collector (PodGC),會將以終止的 pods 清除
PodGC 會清除符合以下條件的 pods:
node.kubernetes.io/out-of-service
的 taint若要更暸解 PodGC 的運作機制的話,就要再看看 pod 的中斷情況了。
pods 可以說是 k8s 最主要的靈魂,負責執行 APP 提供服務
把 pods 的大大小小事情打理好,就可以提昇 pods 的可用度囉
不過也因為牽扯的東西較多,還需要再補助看看其他文件,才能整理出一個比較完整的內容…
https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#pod-disruption-conditions
https://martinheinz.dev/blog/63